package com.lx.demo.b.core.filter;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;
import com.lx.*;
import com.lx.demo.b.utils.MDCUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;

/**
 * Created by lanyxp on 2020/9/11
 * <p>
 * 全局异常对异常进行相关信息记录
 *
 * @see com.alibaba.dubbo.validation.filter
 * </p>
 */
@Slf4j
@Activate(group = {Constants.PROVIDER, Constants.CONSUMER}, order = -1)
public class DubboLogFilter implements Filter {
    private static final String SIDE = "side";
    private static final String PROVIDER = "provider";

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        Result result = null;
        final long start = System.currentTimeMillis();
        try {
            if (invocation.getArguments()[0] instanceof Request) {
                Request<?> request = (Request<?>) invocation.getArguments()[0];
                String seqNo = request.getHeder().getSeqNo();
                if (StringUtils.isNotEmpty(seqNo)) {
                    MDCUtil.put(seqNo);
                } else {
                    if (StringUtils.isEmpty(MDCUtil.get())) {
                        MDCUtil.put();
                    }
                    final RequestHader heder = request.getHeder();
                    heder.setSeqNo(MDCUtil.get());
                    request.setHeder(heder);
                }
            } else {
                MDCUtil.put();
            }
            result = invoker.invoke(invocation);
            this.consleLog(invoker, invocation, result, System.currentTimeMillis() - start);
            final Throwable ex = result.getException();
            if (ObjectUtil.isNotNull(ex)) {
                if (ex instanceof MsagesException) {
                    log.warn("dubbo 服务执行调用失败 code:===>>>:{} mesage:===>>>:{}", ((MsagesException) ex).getCode(), ex.getMessage());
                    return new RpcResult(Remessages.fail(((MsagesException) ex).getCode(), ex.getMessage()));
                } else {
                    log.error("dubbo 服务未知异常:" + ex);
                    return new RpcResult(Remessages.fail(CommonResultCode.DUBBO_SERVICE_UNKNOWN_EXCEPTION));
                }
            }
        } catch (MsagesException me) {
            log.error("dubbo 服务执行调用失败 code==>>>:{} mesage==>>>:{}", ((MsagesException) me).getCode(), me.getMessage());
            return new RpcResult(Remessages.fail(((MsagesException) me).getCode(), me.getMessage()));
        } catch (Exception e) {
            log.error("dubbo执行服务系统异常:[{}]", e);
            return new RpcResult(Remessages.fail(CommonResultCode.DUBBO_SERVICE_UNKNOWN_EXCEPTION));
        } finally {
            final String side = invoker.getUrl().getParameter(SIDE);
            if (ObjectUtil.equal(PROVIDER, side)) {
                MDCUtil.clear();
            }
        }
        return result;
    }

    /****
     *
     * @param invoker
     * @param invocation
     * @param result
     * @param time
     */
    private void consleLog(Invoker<?> invoker, Invocation invocation, Result result, long time) {
        if (ObjectUtil.isNull(invoker.getUrl())) {
            return;
        }
        Object resultData = null;
        Throwable throwable = null;
        if (ObjectUtil.isNotNull(result)) {
            resultData = result.getValue() instanceof String ? result.getValue() : JSONUtil.toJsonStr(result.getValue());
            throwable = result.getException();
        }
        try {
            log.info("[{}] :[接口: {}] [方法: {}] [参数: {}] [响应: {}] [耗时: {}] [异常: {}]", invoker.getUrl().getParameter(SIDE), invoker.getInterface().getTypeName(), invocation.getMethodName(), toJSONarray(invocation.getArguments()), resultData, time, throwable);
        } catch (Exception e) {
            log.error("dubbo 日志错误:{}", e);
        }
    }

    private String[] toJSONarray(Object[] arguments) {
        ArrayList<String> list = new ArrayList(arguments.length);
        for (Object argument : arguments) {
            if (argument instanceof String) {
                list.add((String) argument);
            } else {
                list.add(JSONUtil.toJsonStr(arguments));
            }
        }
        return list.toArray(new String[0]);
    }
}
